package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"strconv"
	"time"

	"github.com/tealeg/xlsx"
)

var options = &Options{}

const usage = `
选项：
  -h, --help
      打印使用帮助信息

动作选项：
  --remove-duplicate
        移除重复行，针对 -f1 所指定的文件，根据 -dd 参数指定的时间差判定是否重复

本地文件模式选项:
  -f1 string
        第一个文件名
  -f2 string
        第二个文件名
  -dd int
        误差，单位秒，默认值为 10

使用例子：
./taide0310.exe -f1 abc.xlsx -f2 def.xlsx -dd 10
./taide0310.exe --remove-duplicate -f1 abc.xlsx -dd 10
`

func init() {
	flag.StringVar(&options.Filename1, "f1", "", "")
	flag.StringVar(&options.Filename2, "f2", "", "")
	flag.IntVar(&options.Dd, "dd", 10, "")
	flag.BoolVar(&options.RemoveDuplicate, "remove-duplicate", false, "")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "使用帮助: %s [选项]\n", os.Args[0])
		fmt.Fprintln(os.Stderr, "选项:")
		fmt.Fprint(os.Stderr, usage)
	}

	// 检查是否传递了-h或--help参数
	if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
		flag.Usage() // 打印使用帮助信息
		return
	}

	// 解析命令行参数
	flag.Parse()
	if len(options.Filename1) == 0 {
		fmt.Fprintf(os.Stderr, "f1 字段不能为空")
		flag.Usage() // 打印使用帮助信息
		os.Exit(1)
	}
	if !options.RemoveDuplicate && len(options.Filename2) == 0 {
		fmt.Fprintf(os.Stderr, "f2 字段不能为空")
		flag.Usage() // 打印使用帮助信息
		os.Exit(1)
	}

	b, err := json.Marshal(options)
	if err != nil {
		fmt.Fprintf(os.Stderr, "解析错误：%s", err.Error())
		flag.Usage() // 打印使用帮助信息
		os.Exit(1)
	}
	log.Println("你输入的选项：options =", string(b))
}

func main() {
	if options.RemoveDuplicate {
		handleRemoveDuplicate()
	} else {
		handleMergeTwoFile()
	}
}

func handleRemoveDuplicate() {
	// 1 读取Excel文件，解析成数组A
	A, titleRowA := parseExcel(options.Filename1, "f1")

	// 2 过滤数组A，把里面重复的行删去，生成数组A2，被删除的行，生成数组A3
	A2 := make([]RecordXlsx, 0)
	A3 := make([]RecordXlsx, 0)
	i, j := 0, 0
	dd := float64(options.Dd)
	for i < len(A) - 1 {
		j = i + 1
		for j < len(A) {
			result := math.Abs(float64(A[j].Ts.Unix()) - float64(A[i].Ts.Unix()))
			if result < dd {
				A3 = append(A3, A[j-1])
				j++
				continue
			}
			break
		}
		A2 = append(A2, A[j-1])
		i = j
	}
	// 添加最后一行
	if i != len(A) {
		A2 = append(A2, A[i])
	}

	// 3 写到文件
	writeFile("out/f1.xlsx", titleRowA, A2)
	writeFile("out/f1-removed.xlsx", titleRowA, A3)
}

func handleMergeTwoFile() {
	// 1 分别读取两个Excel文件，并解析成数组A和B，内部元素需要时间、来源、原始行
	A, titleRowA := parseExcel(options.Filename1, "f1")
	B, titleRowB := parseExcel(options.Filename2, "f2")

	// b, _ := json.Marshal(&records[0])
	log.Println("检查输出第一行时间字段", A[0].Ts, "标题", titleRowA.Cells)
	log.Println("检查输出第一行时间字段", B[0].Ts, "标题", titleRowB.Cells)

	// 2 时间字段对比，在十秒内的认为是相同时间，把交集生成数组C，把A和B去掉C的部分生成A2和B2
	C, A2, B2 := make([]RecordXlsxMerge, 0), make([]RecordXlsx, 0), make([]RecordXlsx, 0)
	i, j := 0, 0
	for i < len(A) && j < len(B) {
		a := A[i]
		b := B[j]
		result := math.Abs(float64(a.Ts.Unix()) - float64(b.Ts.Unix()))
		if result < float64(options.Dd) {
			C = append(C, RecordXlsxMerge{R1: a, R2: b, Result: result})
			i++
			j++
			continue
		}
		if a.Ts.Unix() < b.Ts.Unix() {
			i++
			A2 = append(A2, a)
			continue
		}
		j++
		B2 = append(B2, b)
	}
	for i < len(A) {
		A2 = append(A2, A[i])
		i++
	}
	for j < len(B) {
		B2 = append(B2, B[j])
		j++
	}
	log.Println("对比结束，近似时间数组C的长度为", len(C), "\n数组A2的长度为", len(A2), "\n数组B2的长度为", len(B2))

	// 3 将数组A2，B2和C 生成新的 excel 文件，完成
	writeMergeFile("out/f1-f2.xlsx", titleRowA, titleRowB, C)
	writeFile("out/f1.xlsx", titleRowA, A2)
	writeFile("out/f2.xlsx", titleRowB, B2)

}

func writeFile(filename string, titleRowA *xlsx.Row, A []RecordXlsx) {
	checkOutDir()

	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		fmt.Println(err.Error())
	}
	// 标题
	row := sheet.AddRow()
	for _, r := range titleRowA.Cells {
		cell := row.AddCell()
		cell.Value = r.Value
	}
	for _, r := range A {
		row := sheet.AddRow()
		for _, a := range r.Entity.Cells {
			cell := row.AddCell()
			cell.Value = a.Value
		}
	}
	err = file.Save(filename)
	if err != nil {
		fmt.Println(err.Error())
	}
}

// 检查是否有 out 目录
func checkOutDir() {
	// 检查 out 目录
	if !fileExists("out") {
		err := os.Mkdir("out", 0755)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func writeMergeFile(filename string, titleRowA, titleRowB *xlsx.Row, C []RecordXlsxMerge) {
	checkOutDir()

	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		fmt.Println(err.Error())
	}
	// 标题
	row := sheet.AddRow()
	for _, r := range titleRowA.Cells {
		cell := row.AddCell()
		cell.Value = r.Value
	}
	cell := row.AddCell()
	cell.Value = "相差时间"
	for _, r := range titleRowB.Cells {
		cell := row.AddCell()
		cell.Value = r.Value
	}
	// 如果第一个表格标题列数比行列数的长度要长，则需要在第二个表格前填充对应的空白值
	titleRowALen := len(titleRowA.Cells)
	for _, r := range C {
		row := sheet.AddRow()
		for _, a := range r.R1.Entity.Cells {
			cell := row.AddCell()
			cell.Value = a.Value
		}
		if titleRowALen > len(r.R1.Entity.Cells) {
			for i := 0; i < titleRowALen - len(r.R1.Entity.Cells); i++ {
				cell := row.AddCell()
				cell.Value = ""
			}
		}
		cell := row.AddCell()
		cell.Value = strconv.FormatInt(int64(r.Result), 10)
		for _, a := range r.R2.Entity.Cells {
			cell := row.AddCell()
			cell.Value = a.Value
		}
	}
	err = file.Save(filename)
	if err != nil {
		fmt.Println(err.Error())
	}
}

func parseExcel(filename, origin string) ([]RecordXlsx, *xlsx.Row) {
	log.Println("开始解析", filename)
	workBook, err := xlsx.OpenFile(filename)
	if err != nil {
		panic(err)
	}
	records := make([]RecordXlsx, 0)
	var titleRow *xlsx.Row

	for _, sheet := range workBook.Sheets {
		if sheet.MaxRow == 0 {
			continue
		}
		log.Println("表名称", sheet.Name, "最大行号", sheet.MaxRow)

		for _, row := range sheet.Rows {
			// 首行为标题行
			if titleRow == nil {
				titleRow = row
				continue
			}
			d := row.Cells[1].String()
			if len(d) == 5 {
				d = transExcelOriDate(d)
			}
			t := row.Cells[2].String()
			if len(d) == 0 {
				continue
			}
			ts, e := time.Parse("2006-01-02 15:04:05", d+" "+t)
			if e != nil {
				ts, e = time.Parse("2006/1/2 15:04:05", d+" "+t)
				if e != nil {
					log.Println("时间字段解析失败， field=", d+" "+t)
					continue
				}
			}
			records = append(records, RecordXlsx{Ts: ts, Origin: origin, Entity: row})
		}
	}
	log.Println("解析成功，行数为", len(records))
	return records, titleRow
}

// 将 excle 的日期格式转为 yyyy-MM-dd
// 比如 45383 转为 2024-04-01
var oriDateTime = time.Date(1900, time.January, 1, 0, 0, 0, 0, time.Local)
func transExcelOriDate(excelDate string) string {
	d, e := strconv.Atoi(excelDate)
	if e != nil {
		return ""
	}
	t2 := oriDateTime.Add(time.Hour * 24 * time.Duration(d-1))
	return t2.Format("2006-01-02")
}

func fileExists(filename string) bool {
	_, err := os.Stat(filename)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}
